package geometry;

import hulppakket.Intersection;
import hulppakket.Point3f;
import hulppakket.Ray;
import hulppakket.TexCoord2f;
import hulppakket.Vector3f;
import materials.Material;
import textures.Texture;
import transformation.Matrix4f;

public class Triangle implements Primitive {

	public Vector3f normalA, normalB, normalC;
	public Shape parent;
	public Point3f pointA, pointB, pointC;
	public TexCoord2f textureCoordinateA, textureCoordinateB,
			textureCoordinateC;
	private BoundingBox bbox;

	public Triangle(Shape parent, Point3f pointA, Point3f pointB, Point3f pointC) {
		this.parent = parent;
		this.pointA = pointA;
		this.pointB = pointB;
		this.pointC = pointC;
	}

	public Triangle(Shape parent, Point3f pointA, Point3f pointB,
			Point3f pointC, TexCoord2f texCoordinateA,
			TexCoord2f texCoordinateB, TexCoord2f texCoordinateC,
			Vector3f normalA, Vector3f normalB, Vector3f normalC) {
		this.parent = parent;
		this.pointA = pointA;
		this.pointB = pointB;
		this.pointC = pointC;
		this.textureCoordinateA = texCoordinateA;
		this.textureCoordinateB = texCoordinateB;
		this.textureCoordinateC = texCoordinateC;
		this.normalA = normalA;
		this.normalB = normalB;
		this.normalC = normalC;
	}

	public Triangle(Shape parent, Point3f pointA, Point3f pointB,
			Point3f pointC, Vector3f normalA, Vector3f normalB, Vector3f normalC) {
		this.parent = parent;
		this.pointA = pointA;
		this.pointB = pointB;
		this.pointC = pointC;
		this.normalA = normalA;
		this.normalB = normalB;
		this.normalC = normalC;
	}

	@Override
	public BoundingBox getBoundingBox() {
		if (this.bbox == null) {
			Point3f min, max;
			float x, y, z;
			x = Math.min(pointA.x, pointB.x);
			x = Math.min(x, pointC.x);
			y = Math.min(pointA.y, pointB.y);
			y = Math.min(y, pointC.y);
			z = Math.min(pointA.z, pointB.z);
			z = Math.min(z, pointC.z);
			min = new Point3f(x, y, z);
			x = Math.max(pointA.x, pointB.x);
			x = Math.max(x, pointC.x);
			y = Math.max(pointA.y, pointB.y);
			y = Math.max(y, pointC.y);
			z = Math.max(pointA.z, pointB.z);
			z = Math.max(z, pointC.z);
			max = new Point3f(x, y, z);
			this.bbox = new BoundingBox(min, max);
		}
		return this.bbox;
	}

	public Material getMaterial() {
		return parent.getMaterial();
	}

	public Vector3f getNormal(float alfa, float beta, float gamma) {
		Vector3f result;
		if (normalA != null && normalB != null && normalC != null) {
			result = normalA.scale(alfa).add(
					normalB.scale(beta).add(normalC.scale(gamma)));
			result = result.normalize();
			return result;
		}
		result = new Vector3f(pointB.x - pointA.x, pointB.y - pointA.y,
				pointB.z - pointA.z).crossProduct(new Vector3f(pointC.x
				- pointA.x, pointC.y - pointA.y, pointC.z - pointA.z));
		result = result.normalize();
		return result;
	}

	/**
	 * Zie boek p.208
	 */
	public Intersection intersect(Ray ray, float t0, float t1) {
		float a, b, c, d, e, f, g, h, i, j, k, l, t, beta, gamma, m;
		a = this.pointA.x - this.pointB.x;
		b = this.pointA.y - this.pointB.y;
		c = this.pointA.z - this.pointB.z;
		d = this.pointA.x - this.pointC.x;
		e = this.pointA.y - this.pointC.y;
		f = this.pointA.z - this.pointC.z;
		g = ray.getDirection().x;
		h = ray.getDirection().y;
		i = ray.getDirection().z;
		j = this.pointA.x - ray.getOrigin().x;
		k = this.pointA.y - ray.getOrigin().y;
		l = this.pointA.z - ray.getOrigin().z;
		m = (a * (e * i - h * f)) + (b * (g * f - d * i))
				+ (c * (d * h - e * g));
		m = 1 / m;

		// compute t
		t = -1
				* ((f * (a * k - j * b)) + (e * (j * c - a * l)) + d
						* (b * l - k * c)) * m;
		// check t
		if (t < t0 || t > t1)
			return null;

		// compute gamma
		gamma = ((i * (a * k - j * b)) + (h * (j * c - a * l)) + (g * (b * l - k
				* c)))
				* m;
		// check gamma
		if (gamma < 0 || gamma > 1)
			return null;

		// compute beta
		beta = ((j * (e * i - h * f)) + (k * (g * f - d * i)) + (l * (d * h - e
				* g)))
				* m;
		// check beta
		if (beta < 0 || beta > 1 - gamma)
			return null;
		return new Intersection(this, ray, 1.0f - beta - gamma, beta, gamma, t);
	}

	public void traverseSceneGraph(Matrix4f matrix, Matrix4f inverseMatrix) {
		pointA = new Point3f(matrix.transform(pointA));
		pointB = new Point3f(matrix.transform(pointB));
		pointC = new Point3f(matrix.transform(pointC));
		if (normalA != null && normalB != null && normalC != null) {
			normalA = new Vector3f(inverseMatrix.transform(normalA));
			normalB = new Vector3f(inverseMatrix.transform(normalB));
			normalC = new Vector3f(inverseMatrix.transform(normalC));
		}
	}

	public Texture getTexture() {
		return this.parent.getTexture();
	}
}
